namespace graver
{
    /// <summary>
    /// 语法分析器
    /// </summary>
    public class Parser
    {
        /// <summary>
        /// 解析的 Token 列表
        /// </summary>
        private readonly SyntaxToken[] _tokens;
        /// <summary>
        /// 当前 Token 位置指针
        /// </summary>
        private int _position;

        /// <summary>
        /// 诊断信息
        /// </summary>
        private readonly List<string> _diagnostics;
        public IEnumerable<string> Diagnostics => _diagnostics;
        public Parser(string text)
        {
            var tokens = new List<SyntaxToken>();
            var lexer = new Lexer(text);
            SyntaxToken token;
            do
            {
                token = lexer.NextToken();
                if (SyntaxKind.BadToken == token.Kind || SyntaxKind.SpaceToken == token.Kind)
                {
                    continue; // TODO 错误处理
                }
                tokens.Add(token);
            } while (SyntaxKind.EndOfFileToken != token.Kind);

            _tokens = tokens.ToArray();
            _position = 0;
            _diagnostics = new();
            _diagnostics.AddRange(lexer.Diagnostics);
        }

        private SyntaxToken Peek(int offset)
        {
            var index = _position + offset;
            if (index >= _tokens.Length)
            {
                return _tokens[_tokens.Length - 1];
            }
            return _tokens[index];
        }

        /// <summary>
        /// 当前 Token
        /// </summary>
        private SyntaxToken CurrentToken => Peek(0);

        /// <summary>
        /// 获取当前 Token , 并使 Token 位置指针向后移
        /// </summary>
        private SyntaxToken NextToken()
        {
            var token = CurrentToken;
            _position++;
            return token;
        }

        /// <summary>
        /// 检查当前 Token 是否是指定的类型
        /// </summary>
        /// <param name="kind">待匹配的 Token 类型</param>
        /// <returns></returns>
        private SyntaxToken Match(SyntaxKind kind)
        {
            if (kind == CurrentToken.Kind)
            {
                return NextToken();
            }
            _diagnostics.Add($"Error: Unexpected token <{CurrentToken.Kind}>, expected <{kind}>");
            return new SyntaxToken(kind, CurrentToken.Position, null, null);
        }

        private ExpressionSyntaxNode ParseExpression()
        {
            return ParseTerm();
        }

        /// <summary>
        /// 此法解析器入口
        /// </summary>
        /// <returns>语法树</returns>
        public SyntaxTree Parse()
        {
            var expression = ParseTerm();
            var endOfFileToken = Match(SyntaxKind.EndOfFileToken);
            return new SyntaxTree(_diagnostics, expression, endOfFileToken);
        }

        /// <summary>
        /// 解析加减法
        /// </summary>
        private ExpressionSyntaxNode ParseTerm()
        {
            var left = ParseFactory();
            while (SyntaxKind.PlusToken == CurrentToken.Kind || SyntaxKind.MinusToken == CurrentToken.Kind)
            {
                var operatorToken = NextToken();
                var right = ParseFactory();
                left = new BinaryExpressionSyntax(left, operatorToken, right);
            }
            return left;
        }

        /// <summary>
        /// 解析乘除法
        /// </summary>
        private ExpressionSyntaxNode ParseFactory()
        {
            var left = ParsePrimaryExpressionNode();
            while (SyntaxKind.StarToken == CurrentToken.Kind || SyntaxKind.SlashToken == CurrentToken.Kind)
            {
                var operatorToken = NextToken();
                var right = ParsePrimaryExpressionNode();
                left = new BinaryExpressionSyntax(left, operatorToken, right);
            }
            return left;
        }

        /// <summary>
        /// 解析带括号的表达式
        /// </summary>
        private ExpressionSyntaxNode ParsePrimaryExpressionNode()
        {
            if (SyntaxKind.OpenParenthesisToken == CurrentToken.Kind)
            {
                var left = NextToken();
                var expression = ParseExpression();
                var right = Match(SyntaxKind.CloseParenthesisToken);
                return new ParenthesizedExpressionSyntax(left, expression, right);
            }

            var numberToken = Match(SyntaxKind.NumberToken);
            return new NumberExpressionSyntaxNode(numberToken);
        }
    }
}


